//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of your
// Microsoft Mobile Device 2003 Software Pre-Release Development Agreement
// or other applicable Microsoft license agreement for the software.
// If you did not accept the terms of such a license, you are not
// authorized to use this source code.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:

RilSimTKitByte.cpp

Abstract:


Notes:


--*/

#include "precomp.h"
#include "ccoreutl.h"

// Used for parsing the result for SIMLOCALINFO
HRESULT StringToByteArray(TCHAR *pwsz, BYTE *rgb, int nSize)
{
    int nLen = lstrlen(pwsz);
    int i, j;

    if (nLen > nSize)
    {
        return E_INVALIDARG;
    }

    // If characters are missing, the first few array entries
    // will be considered 0
    memset(rgb, 0, nSize);
    for (i = nSize - 1, j = nLen - 1; j >= 0; i--, j--)
    {
        if ((pwsz[j] >= TEXT('0')) && (pwsz[j] <= TEXT('9')))
        {
            rgb[i] = (BYTE) (pwsz[j] - '0');
        }
        else if ((pwsz[j] >= TEXT('a')) && (pwsz[j] <= TEXT('f')))
        {
            rgb[i] = (BYTE) (pwsz[j] - 'a' + 10);
        }
        else if ((pwsz[j] >= TEXT('A')) && (pwsz[j] <= TEXT('F')))
        {
            rgb[i] = (BYTE) (pwsz[j] - 'A' + 10);
        }
        else
        {
            return E_INVALIDARG;
        }
    }

    return S_OK;
}

TCHAR ByteToTCHAR(BYTE b)
{
    TCHAR tch;

    // We assume b is between 0 and F, of course
    ASSERT((b >= 0) && (b <= 0xf));

    if (b <= 9)
    {
        tch = (TCHAR) (TEXT('0') + b);
    }
    else
    {
        tch = (TCHAR) (TEXT('A') + (b - 10));
    }

    return tch;
}


HRESULT CRilSimToolkitResponse::ByteFormatResponse(const RILSIMTOOLKITRSP* pRsp, const LPBYTE pDetails, const DWORD dwDetailSize, LPBYTE pbDataRsp, DWORD* pdwDataRspSize)
{
    LPBYTE lpb;
    HRESULT hr = S_OK;

    // Assert parameters
    DEBUGCHK(NULL != pRsp);
    DEBUGCHK(NULL != pbDataRsp);
    DEBUGCHK(MAX_BYTE_RSP <= *pdwDataRspSize);  // Make sure there is a enough total room in buffer now,
                                                // so we don't have to check every step of the way.

    // Check parameters
    if (NULL == pRsp ||
        NULL == pbDataRsp ||
        MAX_BYTE_RSP > *pdwDataRspSize)
    {
        DEBUGCHK(FALSE);
        hr = E_INVALIDARG;
        goto Exit;
    }

    // Check dwParams to make sure the necessary fields are supported
    DEBUGCHK(pRsp->dwParams & RIL_PARAM_SIMTKIT_RSP_TYPE);
    DEBUGCHK(pRsp->dwParams & RIL_PARAM_SIMTKIT_RSP_RESPONSE);

    if (!(pRsp->dwParams & RIL_PARAM_SIMTKIT_RSP_TYPE) ||
        !(pRsp->dwParams & RIL_PARAM_SIMTKIT_RSP_RESPONSE))
    {
        hr = E_INVALIDARG;
        goto Exit;
    }

    // Initialize return buffer.
    memset(pbDataRsp, 0, *pdwDataRspSize);
    lpb = pbDataRsp;

    if (SIM_NOTIFY_SETUPMENU_REPLY == pRsp->dwType)
    {
        SIMTKITRSPITEM* pItemDetails = (SIMTKITRSPITEM*)pDetails;

        // Check that the proper details exist.
        if (NULL == pDetails)
        {
            DEBUGCHK(FALSE);
            hr = E_INVALIDARG;
            goto Exit;
        }

        if (!(pItemDetails->dwParams & RIL_PARAM_SIMTKIT_RSP_DETAILS_ITEM_ID))
        {
            DEBUGCHK(FALSE);
            return E_INVALIDARG;
            goto Exit;
        }

        // We're going to be setting up a menu, which is formatted a bit differently than
        // your normal old TERMINAL RESPONSE
        *(lpb++) = MENUSELECTTAG;
        // The length is 7 if no help is requested, otherwise it's 9
        *(lpb++) = (BYTE) ((pRsp->dwResponse == SIM_RESPONSE_OK_HELP) ? 0x09 : 0x07);

        // Device identities
        *(lpb++) = DEVICEIDTAG; // Device identities tag
        *(lpb++) = 0x02;        // Length of what follows
        *(lpb++) = 0x01;        // Source = Keypad
        *(lpb++) = 0x81;        // Destination = SIM

        // Item identifier
        *(lpb++) = ITEMIDTAG;   // Item identifier tag
        *(lpb++) = 0x01;        // Length of what follows
        *(lpb++) = (BYTE) pItemDetails->dwId; // Item which was selected

        // Optional request for help
        if (pRsp->dwResponse == SIM_RESPONSE_OK_HELP)
        {
            *(lpb++) = HELPTAG; // Help request tag
            *(lpb++) = 0x00;    // Length of what follows
        }
    }
    else
    {
        BOOL fCmdDataNeeded = FALSE;

        // OK, let's build up what we want to send down to the SIM
        // Set up command details -- use exactly what we gave to the user
        DEBUGCHK(RIL_PARAM_SIMTKIT_RSP_TAG & pRsp->dwParams);
        DEBUGCHK(RIL_PARAM_SIMTKIT_RSP_ID & pRsp->dwParams);
        DEBUGCHK(RIL_PARAM_SIMTKIT_RSP_TYPE & pRsp->dwParams);
        DEBUGCHK(RIL_PARAM_SIMTKIT_RSP_QUALIFIER & pRsp->dwParams);

        // Send the response command details tag if included.
        if (RIL_PARAM_SIMTKIT_RSP_TAG & pRsp->dwParams)
        {
            *(lpb++) = (BYTE) pRsp->dwTag;
        }
        // If one is not included, default to the command details tag without
        // comprehension set.
        else
        {
            *(lpb++) = COMMANDIDTAG;
        }

        *(lpb++) = 3;   // Length is spec'ed to 3 bytes.

        // Command Details Identifier
        if (RIL_PARAM_SIMTKIT_RSP_ID & pRsp->dwParams)
        {
            *(lpb++) = (BYTE) pRsp->dwId;
        }
        else
        {
            *(lpb++) = 0;
        }

        // Command Details Type
        if (RIL_PARAM_SIMTKIT_RSP_TYPE & pRsp->dwParams)
        {
            *(lpb++) = (BYTE) pRsp->dwType;
        }
        else
        {
            *(lpb++) = 0;
        }

        // Command Details Qualifier
        if (RIL_PARAM_SIMTKIT_RSP_QUALIFIER & pRsp->dwParams)
        {
            *(lpb++) = (BYTE) pRsp->dwQualifier;
        }
        else
        {
            *(lpb++) = 0;
        }

        // Device identities
        *(lpb++) = DEVICEIDTAG; // Device identities tag
        *(lpb++) = 0x02;        // Length of what follows

        if (SIM_NOTIFY_CLOSECHANNEL == pRsp->dwType)
        {
            SIMTKITRSPCLOSECHANNEL* pCloseChannel;

            // Poll interval details are necessary.
            if (NULL == pDetails)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            pCloseChannel = (SIMTKITRSPCLOSECHANNEL*)pDetails;

            // We need the Close channel details for a proper byte response.
            if (NULL == pCloseChannel)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }

            // Make sure all necessary fields are available.
            if ( !(RIL_PARAM_SIMTKIT_RSP_DETAILS_CLOSECHANNEL_ID & pCloseChannel->dwParams) )
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            // Channel ID must be between 0x21 and 0x27
            ASSERT(0x21 <= pCloseChannel->dwChannelId);
            ASSERT(0x27 >= pCloseChannel->dwChannelId);

            *(lpb++) = (BYTE) pCloseChannel->dwChannelId; // Source = Channel ID
        }
        else if(SIM_NOTIFY_RECEIVEDATA == pRsp->dwType)
        {
            SIMTKITRSPRECEIVEDATA* pRcvData;

            // Poll interval details are necessary.
            if (NULL == pDetails)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            pRcvData = (SIMTKITRSPRECEIVEDATA*)pDetails;

            // Make sure all necessary fields are available.
            if ( !(RIL_PARAM_SIMTKIT_RSP_DETAILS_RECEIVEDATA_ID & pRcvData->dwParams) )
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            // Channel ID must be between 0x21 and 0x27
            ASSERT(0x21 <= pRcvData->dwChannelId && 0x27 >= pRcvData->dwChannelId);

            *(lpb++) = (BYTE) pRcvData->dwChannelId; // Source = Channel ID
        }
        else if(SIM_NOTIFY_SENDDATA == pRsp->dwType)
        {
            SIMTKITRSPSENDDATA* pSendData;

            // Poll interval details are necessary.
            if (NULL == pDetails)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            pSendData = (SIMTKITRSPSENDDATA*)pDetails;

            // Make sure all necessary fields are available.
            if ( !(RIL_PARAM_SIMTKIT_RSP_DETAILS_RECEIVEDATA_ID & pSendData->dwParams) )
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }

            // Channel ID must be between 0x21 and 0x27
            ASSERT(0x21 <= pSendData->dwChannelId && 0x27 >= pSendData->dwChannelId);

            *(lpb++) = (BYTE) pSendData->dwChannelId; // Source = Channel ID
        }
        else
        {
            *(lpb++) = 0x82;        // Source = ME
        }

        *(lpb++) = 0x81;        // Destination = SIM


        // Result block

        // Note that additional information is limited to a byte for now
        *(lpb++) = RESULTTAG;

        // Length; 2 if additional info present, 1 if not
        *(lpb++) = ((pRsp->dwParams & RIL_PARAM_SIMTKIT_RSP_ADDITIONALINFO) ?  0x02 : 0x01);   // Length
        *(lpb++) = (BYTE) pRsp->dwResponse;

        // Check whether the response type needs or does not need corresponding user data. This is for responses in the
        // 0X range (where ENDSESSION is 10) or for help requests.
        fCmdDataNeeded = (pRsp->dwResponse < SIM_RESPONSE_OK_ENDSESSION || pRsp->dwResponse == SIM_RESPONSE_OK_HELP);

        // Additional info if present
        if (pRsp->dwParams & RIL_PARAM_SIMTKIT_RSP_ADDITIONALINFO)
        {
            *(lpb++) = (BYTE) pRsp->dwAdditionalInfo;
        }

        // Optional duration -- it's given in millisecond intervals, may be able
        // to specify it in minutes or seconds
        if (fCmdDataNeeded && (SIM_NOTIFY_POLLINTERVAL == pRsp->dwType))
        {
            SIMTKITRSPPOLLINTERVAL* pPoll;

            // Poll interval details are necessary.
            if (NULL == pDetails)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            pPoll = (SIMTKITRSPPOLLINTERVAL*)pDetails;

            // Make sure all necessary fields are available.
            if ( !(RIL_PARAM_SIMTKIT_RSP_DETAILS_POLL_ALL & pPoll->dwParams) )
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }

            // The value can't be more than 255
            if (pPoll->dwInterval > 255)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }

            *(lpb++) = DURATIONTAG;               // Duration
            *(lpb++) = 0x02;                      // Length
            *(lpb++) = (BYTE) pPoll->dwUnit;      // Unit
            *(lpb++) = (BYTE) pPoll->dwInterval;  // Value
        }

        // Optional text string
        // Form text tag for commands that require it.
        if ( fCmdDataNeeded &&
             ((SIM_NOTIFY_GETINKEY == pRsp->dwType) ||
              (SIM_NOTIFY_GETINPUT == pRsp->dwType) ||
              (SIM_NOTIFY_SENDUSSD == pRsp->dwType)) )
        {
            BYTE bEncode = SIMTKIT_TEXT_ENCODING_UCS2; // Default text encoding to UCS2
            LPWSTR wszText;
            DWORD dwTextSize;


            *(lpb++) = TEXTTAG;                     // Tag

            // Currently all other commands are sending in a Text structure.
            SIMTKITRSPTEXT* pText = (SIMTKITRSPTEXT*)pDetails;

            // Assume there is no text string.
            wszText = NULL;
            if (NULL != pText)
            {
                // Details exist. If text does not exist in the details, then
                // there is no text string.
                if ( (RIL_PARAM_SIMTKIT_RSP_DETAILS_TEXT_TEXTOFFSET & pText->dwParams) &&
                     (RIL_PARAM_SIMTKIT_RSP_DETAILS_TEXT_TEXTSIZE & pText->dwParams) )
                {
                    // Some text exists.
                    dwTextSize = pText->dwTextSize;

                    if (0 != dwTextSize)
                    {
                        wszText = (LPWSTR) ((LPBYTE)pText + pText->dwTextOffset);
                        // Set the encoding method if specified.
                        if (RIL_PARAM_SIMTKIT_RSP_DETAILS_TEXT_DCS & pText->dwParams)
                        {
                            bEncode = (BYTE) pText->dwDCS;
                        }
                    }
                }
            }

            if (NULL == wszText)
            {
                // Yes, this is valid -- set length to 0
                *(lpb++) = 0x00;
            }
            else
            {
                BYTE rgbText[255];
                UINT uiLen, uiSpaceLeft;

                // Now, convert this, please -- how much space is left in this array?  Remember, we can
                // only send down 255 bytes.  I'm assuming that if text is set, nothing else is (e.g. this
                // is the last thing in the response).  Also subtract a byte for the encoding scheme and
                // a byte for the length (actually 2 for the length if it's more than 0x80)
                uiLen = lstrlen(wszText);
                uiSpaceLeft = *pdwDataRspSize - (lpb - pbDataRsp) - 2;
                if (uiSpaceLeft >= 0x80)
                {
                    // It will take 2 bytes for the length in this case
                    uiSpaceLeft--;
                }

                if (bEncode == SIMTKIT_TEXT_ENCODING_UCS2)
                {
                    ConvertUnicodeToUCS2(wszText, uiLen, (const LPSTR) rgbText, uiSpaceLeft, uiLen);
                }
                else if (bEncode == SIMTKIT_TEXT_ENCODING_UNPACKED)
                {
                    ConvertUnicodeToUnpackedGSM(wszText, uiLen, (const LPSTR) rgbText, uiSpaceLeft, uiLen);
                }
                else
                {
                    ASSERT(bEncode == SIMTKIT_TEXT_ENCODING_PACKED);
                    ConvertUnicodeToGSM(wszText, uiLen, (const LPSTR) rgbText, uiSpaceLeft, uiLen, TRUE);
                }

                // Add one to the length (to take into account the encoding byte)
                uiLen++;

                // OK, now set the length
                // If the length is over 0x80, we need to encode it in two bytes,
                // otherwise, we can just encode it in one byte
                if (uiLen < 0x80)
                {
                    *(lpb++) = (BYTE) uiLen;
                }
                else
                {
                    // Make the next byte 0x81
                    *(lpb++) = 0x81;
                    *(lpb++) = (BYTE) uiLen;
                }

                // Set the data coding scheme
                *(lpb++) = bEncode;

                // And copy over the text string
                memcpy(lpb, rgbText, uiLen - 1);
                lpb += (uiLen - 1);
            }
        }

        // Optional item identifier
        if (fCmdDataNeeded && (SIM_NOTIFY_SELECTITEM == pRsp->dwType))
        {
            SIMTKITRSPITEM* pItem = (SIMTKITRSPITEM*)pDetails;

            // Check to make sure there is are item details.
            if (NULL == pItem)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }
            // Make sure the proper information is available in the details
            if ( !(RIL_PARAM_SIMTKIT_RSP_DETAILS_ITEM_ID & pItem->dwParams) )
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }

            // Easy
            *(lpb++) = ITEMIDTAG;                       // Tag
            *(lpb++) = 0x01;                            // Length
            *(lpb++) = (BYTE) pItem->dwId;              // Item ID
        }

        // Optional local information
        if (fCmdDataNeeded && (SIM_NOTIFY_LOCALINFO == pRsp->dwType))
        {
            BYTE rgbDigit[15];
            LPSIMLOCALINFO psli;

            // Local info detailed information is needed.
            if (NULL == pDetails)
            {
                DEBUGCHK(FALSE);
                hr = E_INVALIDARG;
                goto Exit;
            }

            psli = (SIMLOCALINFO*) pDetails;

            if ((psli->dwParams & SIM_PARAM_SIMLOCALINFO_CELLID) &&
                (psli->dwParams & SIM_PARAM_SIMLOCALINFO_LAC) &&
                (psli->dwParams & SIM_PARAM_SIMLOCALINFO_NUMNAME))
            {
                // Location information
                *(lpb++) = LOCINFOTAG;              // Tag
                *(lpb++) = 0x07;                    // Length

                // For this to be a valid name, it needs to have exactly 5 characters, and they
                // all need to be a single hexadecimal character
                hr = StringToByteArray(psli->lpszNumName, rgbDigit, 5);
                if (FAILED(hr))
                {
                    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Location Information called with invalid name\r\n")));
                    goto Exit;
                }

                // Copy over the bytes of MCC and MNC from the name (see GSM 04.08, 10.5.1.3)
                // First byte -- first two characters (note these are in reverse order, second
                // character is the high half-byte), second byte is third character with 0xf0 in
                // the high bits, third byte is fourth and fifth characters
                *(lpb++) = (BYTE) (rgbDigit[0] + (rgbDigit[1] << 4));
                *(lpb++) = (BYTE) (rgbDigit[2] + 0xf0);
                *(lpb++) = (BYTE) (rgbDigit[3] + (rgbDigit[4] << 4));

                // Copy over the LAC and the CellId
                memcpy(lpb, &(psli->dwLAC), sizeof(DWORD));
                lpb += sizeof(DWORD);
            }

            if (psli->dwParams & SIM_PARAM_SIMLOCALINFO_IMEI)
            {
                // IMEI
                *(lpb++) = IMEITAG;                 // IMEI
                *(lpb++) = 0x08;                    // Length

                // Get 15 digits, please
                hr = StringToByteArray(psli->lpszIMEI, rgbDigit, 15);
                if (FAILED(hr))
                {
                    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 15 digits for IMEI\r\n")));
                    goto Exit;
                }

                // Set these, with the last byte getting an extra 1111
                *(lpb++) = (BYTE) (rgbDigit[1] + (rgbDigit[0] << 4));
                *(lpb++) = (BYTE) (rgbDigit[3] + (rgbDigit[2] << 4));
                *(lpb++) = (BYTE) (rgbDigit[5] + (rgbDigit[4] << 4));
                *(lpb++) = (BYTE) (rgbDigit[7] + (rgbDigit[6] << 4));
                *(lpb++) = (BYTE) (rgbDigit[9] + (rgbDigit[8] << 4));
                *(lpb++) = (BYTE) (rgbDigit[11] + (rgbDigit[10] << 4));
                *(lpb++) = (BYTE) (rgbDigit[13] + (rgbDigit[12] << 4));
                *(lpb++) = (BYTE) (0x0f + (rgbDigit[14] << 4));
            }

            if ((psli->dwParams & SIM_PARAM_SIMLOCALINFO_NMR) &&
                (psli->dwParams & SIM_PARAM_SIMLOCALINFO_BCCH) &&
                (psli->dwParams & SIM_PARAM_SIMLOCALINFO_NUMBCCH))
            {
                DWORD dwBCCHSize;

                // NMR
                *(lpb++) = NMRTAG;                  // NMR
                *(lpb++) = MAXLEN_NMR;              // Length
                memcpy(lpb, psli->rgbNMR, MAXLEN_NMR);
                lpb += MAXLEN_NMR;

                // BCCH
                // Number of BCCH must be 1-3
                if ((psli->dwNumBCCH < 1) || (psli->dwNumBCCH > 3))
                {
                    DEBUGCHK(FALSE);
                    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 1-3 BCCHs\r\n")));
                    hr = E_INVALIDARG;
                    goto Exit;
                }

                *(lpb++) = BCCHTAG;                 // BCCH
                *(lpb++) = (BYTE) (MAXLEN_BCCH * (psli->dwNumBCCH)); // Length
                dwBCCHSize = MAXLEN_BCCH * (psli->dwNumBCCH);
                memcpy(lpb, psli->rgbBCCH, dwBCCHSize);
                lpb += dwBCCHSize;
            }
        }

        // We don't have optional call control or result data object 2
    }// else !fSetupMenuReply

    // Adjust the Data response size parameter.
    *pdwDataRspSize = (LPBYTE)lpb - (LPBYTE)pbDataRsp;

Exit:

    return hr;
}


HRESULT CRilSimToolkitResponse::ByteEventDownload(const DWORD dwEvent, const LPBYTE pData, const DWORD dwDataSize, LPBYTE pbDataRsp, DWORD *pdwDataRspSize)
{
    LPBYTE lpb;
    HRESULT hr = S_OK;

    // Assert parameters
    DEBUGCHK(NULL != pbDataRsp);
    DEBUGCHK(MAX_BYTE_RSP <= *pdwDataRspSize);  // Make sure there is a enough total room in buffer now,
                                                // so we don't have to check every step of the way.

    // Check parameters
    if (NULL == pbDataRsp ||
        MAX_BYTE_RSP > *pdwDataRspSize)
    {
        DEBUGCHK(FALSE);
        hr = E_INVALIDARG;
        goto Exit;
    }

    // Initialize return buffer.
    memset(pbDataRsp, 0, *pdwDataRspSize);
    lpb = pbDataRsp;

    // Start creation of the Event Download envelope command
    *(lpb++) = EVENTDOWNLOADTAG;

    // Hard coded length to 7 bytes. As more events are supported this will
    // need to be dynamic.
    *(lpb++) = (BYTE)0x07;

    // Device identities
    *(lpb++) = DEVICEIDTAG;    // Device identities tag
    *(lpb++) = 0x02;           // Length of what follows
    *(lpb++) = 0x82;           // Source = Terminal
    *(lpb++) = 0x81;           // Destination = SIM

    // Item identifier
    *(lpb++) = EVENTLISTTAG;   // Event list tag
    *(lpb++) = 0x01;           // Length of what follows
    *(lpb++) = (BYTE) dwEvent; // Event that occurred.

    // Adjust the Data response size parameter.
    *pdwDataRspSize = (LPBYTE)lpb - (LPBYTE)pbDataRsp;

Exit:

    return hr;
}


/****************************************************************************

    FUNCTION:   ByteParseOpenChannelCS

    PURPOSE:    Parses and incoming Open Channel CS command

    RETURNS:    HRESULT

****************************************************************************/
HRESULT CRilSimToolkitCommand::ByteParseOpenChannelCS(DWORD* pdwRetVal)
{
    HRESULT hr = S_OK;

    // The Open Channel CS TLV tags should be:
    // Address,(Subaddress),(Duration1),(Duration2), BearerDescription,
    // BufferSize,(LocalAddress),(UserLogin:Text),(UserPassword:Text),
    // (InterfaceTransportLevel),(DestAddress)

    // Address
    if (ADDRESSTAG != (*(m_lpbParse) & 0x7f))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : SIMTKit: Mandatory TLV %x not found. Found: %x\r\n"), ADDRESSTAG, (*(m_lpbParse) & 0x7f)));
        *pdwRetVal = SIM_RESPONSE_ERR_COMMANDDATA;
        hr = E_FAIL;
        goto Exit;
    }
    m_lpbParse++;
    m_dwParseLen--;
    hr = ReadAddress();
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), ADDRESSTAG));
        *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
        hr = E_FAIL;
        goto Exit;
    }

    // (Subaddress)
    if (SUBADDRTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadSubAddress();
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), SUBADDRTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (Duration1)
    if (DURATIONTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadDuration();
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), DURATIONTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (Duration2)
    if (DURATIONTAG == (*(m_lpbParse) & 0x7f))
    {
        // The ReadDuration function reads the duration into a set class variable which is currently
        // set to Duration1. Save that setting to swap the results after a successful read.
        DWORD dwDurationTemp = m_dwDuration;

        m_lpbParse++;
        m_dwParseLen--;
        m_dwDuration = SIMTONE_DEFAULTDURATION;
        hr = ReadDuration();
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), DURATIONTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
        else
        {
            // Swap duration 2 and restore duration 1.
            m_dwDuration2 = m_dwDuration;
            m_dwDuration = dwDurationTemp;
        }
    }

    // Bearer description
    if (BEARERDESCRIPTTAG != (*(m_lpbParse) & 0x7f))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : SIMTKit: Mandatory TLV %x not found. Found: %x\r\n"), BEARERDESCRIPTTAG, (*(m_lpbParse) & 0x7f)));
        *pdwRetVal = SIM_RESPONSE_ERR_COMMANDDATA;
        hr = E_FAIL;
        goto Exit;
    }
    m_lpbParse++;
    m_dwParseLen--;
    hr = ReadBearerDescription();
    if (FAILED(hr))
    {
       RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), BEARERDESCRIPTTAG));
        *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
        hr = E_FAIL;
        goto Exit;
    }

    // Buffer Size
    if (BUFFERSIZETAG != (*(m_lpbParse) & 0x7f))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : SIMTKit: Mandatory TLV %x not found. Found: %x\r\n"), BUFFERSIZETAG, (*(m_lpbParse) & 0x7f)));
        *pdwRetVal = SIM_RESPONSE_ERR_COMMANDDATA;
        hr = E_FAIL;
        goto Exit;
    }
    m_lpbParse++;
    m_dwParseLen--;
    hr = ReadBufferSize();
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), BUFFERSIZETAG));
        *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
        hr = E_FAIL;
        goto Exit;
    }

    // (LocalAddress:Other Address)
    if (OTHERADDRTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonByteAndByteList(&m_dwLocalAddrType, &m_lpbLocalAddr, &m_dwLocalAddrLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), OTHERADDRTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (User Login:Text)
    if (TEXTTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonTextString(&m_pwszLogin, &m_dwLoginLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), TEXTTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (UserPassword:Text)
    if (TEXTTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonTextString(&m_pwszPassword, &m_dwPasswordLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), TEXTTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (InterfaceTransportLevel)
    if (TRANSPORTLVLTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadTransportLevel();
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), TEXTTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (DestAddress:Other Address)
    if (OTHERADDRTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonByteAndByteList(&m_dwDestAddrType, &m_lpbDestAddr, &m_dwDestAddrLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), OTHERADDRTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

Exit:
return hr;
}

/****************************************************************************

    FUNCTION:   ByteParseOpenChannelGPRS

    PURPOSE:    Parses and incoming Open Channel CS command

    RETURNS:    HRESULT

****************************************************************************/
HRESULT CRilSimToolkitCommand::ByteParseOpenChannelGPRS(DWORD* pdwRetVal)
{
    HRESULT hr = S_OK;

    // The Open Channel GPRS TLV tags should be:
    // BearerDescription,BufferSize,(APN),(LocalAddress),
    // (InterfaceTransportLevel),(DestAddress)

    // Bearer description
    if (BEARERDESCRIPTTAG != (*(m_lpbParse) & 0x7f))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : SIMTKit: Mandatory TLV %x not found. Found: %x\r\n"), BEARERDESCRIPTTAG, (*(m_lpbParse) & 0x7f)));
        *pdwRetVal = SIM_RESPONSE_ERR_COMMANDDATA;
        hr = E_FAIL;
        goto Exit;
    }
    m_lpbParse++;
    m_dwParseLen--;
    hr = ReadBearerDescription();
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), BEARERDESCRIPTTAG));
        *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
        hr = E_FAIL;
        goto Exit;
    }

    // Buffer Size
    if (BUFFERSIZETAG != (*(m_lpbParse) & 0x7f))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : SIMTKit: Mandatory TLV %x not found. Found: %x\r\n"), BUFFERSIZETAG, (*(m_lpbParse) & 0x7f)));
        *pdwRetVal = SIM_RESPONSE_ERR_COMMANDDATA;
        hr = E_FAIL;
        goto Exit;
    }
    m_lpbParse++;
    m_dwParseLen--;
    hr = ReadBufferSize();
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), BUFFERSIZETAG));
        *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
        hr = E_FAIL;
        goto Exit;
    }

    // (APN)
    if (NETACCESSNAMETAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonByteList(&m_lpbAccessName, &m_dwAccessNameLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), NETACCESSNAMETAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (LocalAddress:Other Address)
    if (OTHERADDRTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonByteAndByteList(&m_dwLocalAddrType, &m_lpbLocalAddr, &m_dwLocalAddrLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), OTHERADDRTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (InterfaceTransportLevel)
    if (TRANSPORTLVLTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadTransportLevel();
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), TEXTTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

    // (DestAddress:Other Address)
    if (OTHERADDRTAG == (*(m_lpbParse) & 0x7f))
    {
        m_lpbParse++;
        m_dwParseLen--;
        hr = ReadCommonByteAndByteList(&m_dwDestAddrType, &m_lpbDestAddr, &m_dwDestAddrLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Function for TLV %x failed\r\n"), OTHERADDRTAG));
            *pdwRetVal = SIM_RESPONSE_ERR_VALUESMISSING;
            hr = E_FAIL;
            goto Exit;
        }
    }

Exit:
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadDialNumberString

    PURPOSE:    Reads a dial number string, including DTMF separators and characters
                    This is used for both the Address and Short String

    PARAMETERS: ppwsz - The string to set
                pdwLen - The length to set

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadDialNumberString(TCHAR **ppwsz, DWORD *pdwLen)
{
    HRESULT hr = S_OK;
    DWORD dwLength;
    TCHAR *pwszAddress;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadDialNumberString\r\n")));

    DEBUGCHK(ppwsz);
    DEBUGCHK(pdwLen);

    if (NULL == ppwsz || 0 >= pdwLen)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadDialNumberString return parameter is null.\r\n"), hr));
        goto Exit;
    }

    DEBUGCHK(*ppwsz == NULL);
    DEBUGCHK(*pdwLen == 0);

    // OK, read in the length
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadCmdLength failed %x\r\n"), hr));
        goto Exit;
    }

    // OK, we should have this much space left
    if (m_dwParseLen < dwLength)
    {
        // Nope, not enough space left
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Needed %d bytes, only have %d %x\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Now for the actual address string, note that dwLength includes the previous byte
    // Also note that each byte is actually two digits
    pwszAddress = new TCHAR[2 * dwLength + 1];
    if (!pwszAddress)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Ran out of memory\r\n")));
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    hr = ConvertBytesToDialString(m_lpbParse, dwLength, pwszAddress, &m_dwAddrType, &m_dwNumPlan);
    if (FAILED(hr))
    {
        goto Exit;
    }

    // We're done!
    m_lpbParse += dwLength;
    m_dwParseLen -= dwLength;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Address %s, length remaining %d\r\n"), pwszAddress, m_dwParseLen));

    // Set variables
    *ppwsz = pwszAddress;
    *pdwLen = (lstrlen(pwszAddress) + 1) * sizeof(TCHAR);

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadDialNumberString\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadEFADNText

    PURPOSE:    Helper function for any TLV that codes its text the same way
                    as EF-ADN as defined in 11.11, annex B.  Many strings are
                    coded this way, but not (surprise surprise) the string
                    from DISPLAY TEXT, which is coded a different way

    PARAMETERS: ppwszText - Pointer to string which we allocate -- will hold
                                the result
                dwLength - The length of the data that follows
                pdwUsedLen - Set to the size in bytes of the returned string

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadEFADNText(TCHAR **ppwszText, DWORD dwLength, DWORD *pdwUsedLen)
{
    TCHAR *pwsz = NULL;
    HRESULT hr = S_OK;
    BYTE bEncode;
    BOOL fRetVal;
    UINT uiUsedLen = 0, uiBasePage;
    DWORD dwTextLen;
    LPBYTE lpbTemp;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadEFADNText\r\n")));

    DEBUGCHK(ppwszText);

    if (NULL == ppwszText)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadEFADNText return parameter is null.\r\n")));
        hr = E_FAIL;
        goto Exit;
    }

    DEBUGCHK(*ppwszText == NULL);

    if (m_dwParseLen < dwLength)
    {
        // Well, that can't be
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Allocate enough space for the worst case, which would be the length of what remains
    // (in the unpacked case) -- we'll be off by at most 5 characters
    pwsz = new TCHAR[dwLength + 1];
    if (!pwsz)
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    // Special case -- the length is 0
    if (dwLength == 0)
    {
        uiUsedLen = 0;
    }
    else
    {
        // This string is coded as in GSM 11.11, Annex B.  The first byte either tells us how the
        // rest of this string is encoded or is the first character of the string itself
        bEncode = *m_lpbParse;

        switch (bEncode)
        {
            case 0x80:
                // UCS2 encoding
                // Per GSM 11.11, Annex B, we should ignore any FF characters at the end of this string
                lpbTemp = m_lpbParse + dwLength - 1;
                while ((lpbTemp > m_lpbParse) && (*lpbTemp == 0xFF))
                {
                    lpbTemp--;
                }

                // Start parsing after the encoding byte, up to the last non-0xFF byte.
                fRetVal = ConvertUCS2ToUnicode((LPCSTR) (m_lpbParse + 1), lpbTemp - m_lpbParse, pwsz, dwLength, uiUsedLen);
                break;

            case 0x81:
                // Mixed encoding -- the next byte is the length of the string, the byte after that
                // gives us information about the base code page, and then comes the string
                dwTextLen = (DWORD) *(m_lpbParse + 1);
                if (dwTextLen > (dwLength - 4))
                {
                    // That doesn't make sense
                    hr = E_INVALIDARG;
                    goto Exit;
                }

                // Base page is bits 15 to 7
                uiBasePage = ((DWORD) *(m_lpbParse + 2)) << 7;
                fRetVal = ConvertGSMMixedToUnicode(uiBasePage, (LPCSTR) (m_lpbParse + 3), dwTextLen,
                                                    pwsz, dwLength, uiUsedLen);
                break;

            case 0x82:
                // Mixed encoding -- the next byte is the length of the string, the next two bytes
                // give us information about the base code page, and then comes the string
                dwTextLen = (DWORD) *(m_lpbParse + 1);
                if (dwTextLen > (dwLength - 5))
                {
                    // That doesn't make sense
                    hr = E_INVALIDARG;
                    goto Exit;
                }

                // Base page is bits 15 to 7
                uiBasePage = (((DWORD) *(m_lpbParse + 2)) << 8) + ((DWORD) *(m_lpbParse + 3));
                fRetVal = ConvertGSMMixedToUnicode(uiBasePage, (LPCSTR) (m_lpbParse + 4), dwTextLen,
                                                    pwsz, dwLength, uiUsedLen);
                break;

            default:
                // Note that in the default case the encoding byte is part of the string itself
                // This means it's the default GSM 7-bit alphabet, unpacked
                // Per GSM 11.11, Annex B, we should ignore any FF characters at the end of this string
                lpbTemp = m_lpbParse + dwLength - 1;
                while ((lpbTemp >= m_lpbParse) && (*lpbTemp == 0xFF))
                {
                    lpbTemp--;
                }

                // Start parsing at the first byte, up to the last non-0xFF byte.
                fRetVal = ConvertUnpackedGSMToUnicode((LPCSTR) m_lpbParse, (lpbTemp + 1 - m_lpbParse), pwsz, dwLength, uiUsedLen);
#ifdef EMP_DRIVER
                // On EMP platform bEncode byte for UCS2 may not be present, so let's move on and parse the remaining part
                if (!fRetVal)
                {
                    fRetVal = ConvertUCS2ToUnicode((LPCSTR) m_lpbParse, lpbTemp + 1 - m_lpbParse, pwsz, dwLength, uiUsedLen);
                }
#endif
                break;
        }

        if (!fRetVal)
        {
            // This wasn't set
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Failed to parse bytes, encoding %x\r\n"), bEncode));
            hr = E_INVALIDARG;
            goto Exit;
        }
    }

    // OK, NULL terminate this
    pwsz[uiUsedLen] = TEXT('\0');
    m_lpbParse += dwLength;
    m_dwParseLen -= dwLength;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Text %s, length remaining %d\r\n"), pwsz, m_dwParseLen));

Exit:
    if (FAILED(hr))
    {
        *pdwUsedLen = 0;
        delete [] pwsz;
        pwsz = NULL;
    }
    else
    {
        *pdwUsedLen = (uiUsedLen + 1) * sizeof(TCHAR);
    }

    if (NULL != ppwszText)
    {
        *ppwszText = pwsz;
    }

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadEFADNText\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadCmdLength

    PURPOSE:    The length can be either 1 or 2 bytes (per Annex D, 11.14)

    PARAMETERS: pdwLength - The length

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCmdLength(DWORD *pdwLength)
{
    HRESULT hr = E_FAIL;
    DWORD dwLength = 0;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCmdLength\r\n")));

    // m_dwParseLen better be at least 1
    if (m_dwParseLen >= 1)
    {
        // Next comes the length of what follows, it can be 1 or 2 bytes in length
        dwLength = (DWORD) (*(m_lpbParse++));
        m_dwParseLen--;
        if (dwLength == 0x81)
        {
            // This means the length is really in the second byte -- better have another byte
            if (m_dwParseLen >= 1)
            {
                dwLength = (DWORD) (*(m_lpbParse++));
                m_dwParseLen--;
                if ((dwLength >= 0x80) && (dwLength <= 0xff))
                {
                    // This is good
                    hr = S_OK;
                }
                else
                {
                    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Length of %d in second byte invalid\r\n"), dwLength));
                }
            }
        }
        else if ((dwLength >= 0x00) && (dwLength <= 0x7F))
        {
            // This is good
            hr = S_OK;
        }
        else
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: CmdLength first byte of %d is invalid\r\n"), dwLength));
        }
    }
    else
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Not enough bytes left to read CmdLength\r\n")));
    }

    *pdwLength = dwLength;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCmdLength\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadCommonTextString

    PURPOSE:    Reads a text string

    PARAMETERS: ppwszText - Pointer to the text to read (we allocate it)
                pdwLen - The length of this string, in bytes

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCommonTextString(TCHAR **ppwszText, DWORD *pdwLen)
{
    HRESULT hr = S_OK;
    DWORD dwLength;
    UINT uiLen;
    UINT uiUsedLen = 0;
    BYTE bEncode, bScheme;
    LPCSTR lpcstr;
    bool fRetVal = false;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCommonTextString\r\n")));

    ASSERT(*ppwszText == NULL);

    // First read the length of the text
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadCmdLength failed %x\r\n"), hr));
        goto Exit;
    }

    if (m_dwParseLen < dwLength)
    {
        // Well, that can't be
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    if (!dwLength)
    {
        // We've received a null text string which is valid, No further work is necessary,
        // so just return a positive result.
        hr = S_OK;
        goto Exit;
    }

    // OK, figure out the encoding
    bEncode = *(m_lpbParse++);
    lpcstr = (LPCSTR) m_lpbParse;

    // Allocate enough space -- the worst it could be is 8/7 times the number of characters
    // that are remaining
    uiLen = 1 + ((8 * dwLength) / 7);
    *ppwszText = new TCHAR[uiLen];
    if (!(*ppwszText))
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    // Coded as per SMS Data Coding in 03.38 (section 4)
    // We will set bScheme to 0-3 to indicate how this is encoded, 0 = 8-bit, 1 = default alphabet,
    // 2 = default alphabet uncompressed, 3 = UCS2
    if ((bEncode & 0xf0) == 0xf0)
    {
        bScheme = (BYTE) ((bEncode & 0x04) ? 0x0 : 0x1);
    }
    else if ((bEncode & 0xc0) == 0x00)
    {
        if (bEncode & 0x08)
        {
            // This is UCS2 format
            bScheme = 3;
        }
        else
        {
            // Is it packed or not?
            if (bEncode & 0x20)
            {
                // This is packed format
                bScheme = 1;
            }
            else if (bEncode & 0x04)
            {
                // 8-bit data format
                bScheme = 0;
            }
            else if (bEncode == 0x00)
            {
                // Special case, all bits zero means packed format
                bScheme = 1;
            }
            else
            {
                // Use unpacked format
                bScheme = 2;
            }
        }
    }
    else
    {
        // This shouldn't happen -- assert and assume it's 8bit information
        ASSERT(FALSE);
        bScheme = 0;
    }

    switch (bScheme)
    {
        case 1:
            // This is packed format
            fRetVal = ConvertGSMToUnicode(lpcstr, dwLength - 1, *ppwszText, uiLen, uiUsedLen);
            break;

        // As per 11.14, section 12.15.1, 8-bit data is really meant to be interpreted as unpacked
        // SMS default alphabet.
        case 0:
        case 2:
            // Use unpacked format
            fRetVal = ConvertUnpackedGSMToUnicode(lpcstr, dwLength - 1, *ppwszText, uiLen, uiUsedLen);
            break;

        case 3:
            // This is UCS2 format
            fRetVal = ConvertUCS2ToUnicode(lpcstr, dwLength - 1, *ppwszText, uiLen, uiUsedLen);
            break;

        default:
            // Huh?
            ASSERT(FALSE);
            break;
    }

    if (!fRetVal)
    {
        // There was a problem
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Couldn't convert string with scheme %x\r\n"), bScheme));
        DebugBreak();
        hr = E_FAIL;
        goto Exit;
    }

    // Add the NULL terminator
    (*ppwszText)[uiUsedLen] = TEXT('\0');
    *pdwLen = ((uiUsedLen + 1) * sizeof(TCHAR));
    m_lpbParse += (dwLength - 1);
    m_dwParseLen -= dwLength;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: CommonTextString %s, length remaining %d\r\n"), *ppwszText, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCommonTextString\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadCommonEFADNText

    PURPOSE:    Parses text encoded as per EF-ADN.

    PARAMETERS: ppwszText - Pointer to the text to read (we allocate it)
                pdwLen - The length of this string, in bytes

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCommonEFADNText(TCHAR **ppwszText, DWORD *pdwLen)
{
    HRESULT hr = S_OK;
    DWORD dwLength;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCommonEFADNText\r\n")));

    ASSERT(*ppwszText == NULL);

    // First read the length of the text
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        goto Exit;
    }

    // The text is coded as in EF-ADN
    hr = ReadEFADNText(ppwszText, dwLength, pdwLen);
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Read EFADN Text %s (%x)\r\n"), *ppwszText, hr));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCommonEFADNText\r\n")));

    return hr;
}


/****************************************************************************

    FUNCTION:   ReadCommonByte

    PURPOSE:    Reads a byte.
    PARAMETERS: pbData - Pointer to the Byte

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCommonByte(DWORD *pdwData)
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCommonByte\r\n")));

    // Note, we will now read 2 more bytes, so we'd better have it!
    if (m_dwParseLen < 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 2 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 1
    if (*(m_lpbParse++) != 1)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte is %x, should be 1\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Third byte is data
    *pdwData = (DWORD) *(m_lpbParse++);
    m_dwParseLen -= 2;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Byte is 0x%x, length remaining %d\r\n"), *pdwData, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCommonByte\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadCommonByteList

    PURPOSE:    Reads a list of bytes

    PARAMETERS: ppbData - Pointer to the Bytes (we allocate it)
                pdwLen - The count of bytes

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCommonByteList(BYTE **ppbData, DWORD *pdwLen)
{
    HRESULT hr = S_OK;
    DWORD dwLength;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCommonByteList\r\n")));

    ASSERT(*ppbData == NULL);

    // OK, read in the length
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        goto Exit;
    }

    // OK, we should have this much space left
    if (m_dwParseLen < dwLength)
    {
        // Nope, not enough space left
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    *ppbData = new BYTE[dwLength];
    if (!(*ppbData))
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    memcpy(*ppbData, m_lpbParse, dwLength);
    *pdwLen = dwLength;
    m_lpbParse += dwLength;
    m_dwParseLen -= dwLength;

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCommonByteList\r\n")));
    return hr;
}


/****************************************************************************

    FUNCTION:   ReadCommonByteAndByteList

    PURPOSE:    Reads in a length, followed by a byte of data, followed
                by a bytelist that is length-1.

    PARAMETERS: pbData - Pointer to the Bytes (we allocate it)
                ppbData - Pointer to the Bytes (we allocate it)
                pdwLen - The count of bytes

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCommonByteAndByteList(DWORD* pdwByte, BYTE **ppbData, DWORD *pdwLen)
{
    HRESULT hr = S_OK;
    DWORD dwLength;
    DWORD dwByteListLen;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCommonByteAndByteList\r\n")));

    // OK, read in the length
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        goto Exit;
    }

    // OK, we should have this much space left
    if (m_dwParseLen < dwLength)
    {
        // Nope, not enough space left
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // if length of tag is 0, there is nothing to parse.
    if (0 == dwLength)
    {
        goto Exit;
    }

    // Next byte is the value
    *pdwByte = (*(m_lpbParse++));

    // Size of the byte list is length - 1
    dwByteListLen = dwLength - 1;
    *ppbData = new BYTE[dwByteListLen];
    if (!(*ppbData))
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    memcpy(*ppbData, m_lpbParse, dwByteListLen);
    *pdwLen= dwByteListLen;
    m_lpbParse += dwByteListLen;
    m_dwParseLen -= dwLength;

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCommonByteAndByteList\r\n")));

    return hr;
}

/****************************************************************************

    FUNCTION:   ReadMenuItem

    PURPOSE:    Reads menu items in, setting m_rgpsmi and m_numpsmi

    PARAMETERS: ppData - Pointer to pointer to data
                pdwSpaceLeft - How much space is left, in bytes
                ppsmi - Pointer to pointer to SIMMENUITEM

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadMenuItem()
{
    HRESULT hr = S_OK;
    DWORD dwIdentifier, dwUsedLen, dwLen, dwSize;
    TCHAR *pwszText = NULL;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadMenuItem\r\n")));

    ASSERT((m_rgpsmi == NULL) && (m_numpsmi == 0));

    // Let's go through and read items in -- note that the at least one is MANDATORY, all
    // additional menu items are completely optional.  We should first allocate memory
    m_rgpsmi = new LPSIMMENUITEM[10];
    if (!m_rgpsmi)
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    memset(m_rgpsmi, 0, sizeof(LPSIMMENUITEM)  * 10);

    for (;;)
    {
        // Now, figure out the length of the text and identifier
        hr = ReadCmdLength(&dwLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadCmdLength failed %x\r\n"), hr));
            goto Exit;
        }

        if (0 == dwLen && 0 == m_numpsmi)
        {
            // The first menu item is empty. We're done.
            goto Exit;
        }

        // Are there enough characters left?
        if (dwLen < 2)
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Not even 2 characters left, only %d\r\n"), dwLen));
            hr = E_FAIL;
            goto Exit;
        }

        // OK, let's get the text, it's encoded as is EFADN
        dwIdentifier = (DWORD) *(m_lpbParse++);
        m_dwParseLen--;
        delete pwszText;
        pwszText = NULL;
        hr = ReadEFADNText(&pwszText, (dwLen - 1), &dwUsedLen);
        if (FAILED(hr))
        {
            RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadEFADNText failed %x\r\n"), hr));
            goto Exit;
        }

        // OK, let's store this one -- do we need more memory for our array?
        if (m_numpsmi && ((m_numpsmi % 10) == 0))
        {
            // Yep, re-allocate
            LPSIMMENUITEM *psmiTemp = new LPSIMMENUITEM[m_numpsmi + 10];

            if (!psmiTemp)
            {
                hr = E_OUTOFMEMORY;
                goto Exit;
            }

            memcpy(psmiTemp, m_rgpsmi, m_numpsmi * sizeof(LPSIMMENUITEM));
            delete[] m_rgpsmi;
            m_rgpsmi = psmiTemp;
        }

        // Now we can allocate memory -- note that dwLen is the length of the text plus 1
        dwSize = ROUNDTODWORD(sizeof(SIMMENUITEM)) + ROUNDTODWORD(dwUsedLen);
        m_rgpsmi[m_numpsmi] = NULL;
        m_rgpsmi[m_numpsmi] = (SIMMENUITEM *) new BYTE[dwSize];

        if (NULL != m_rgpsmi[m_numpsmi])
        {
            memset(m_rgpsmi[m_numpsmi], 0, dwSize);
            m_rgpsmi[m_numpsmi]->cbSize = dwSize;
            m_rgpsmi[m_numpsmi]->dwParams = SIM_PARAM_SIMMENUITEM_IDENTIFIER | SIM_PARAM_SIMMENUITEM_TEXTOFFSET |
                                    SIM_PARAM_SIMMENUITEM_TEXTSIZE;
            m_rgpsmi[m_numpsmi]->dwIdentifier = dwIdentifier;
            m_rgpsmi[m_numpsmi]->dwTextSize = dwUsedLen;
            m_rgpsmi[m_numpsmi]->dwTextOffset = ROUNDTODWORD(sizeof(SIMMENUITEM));
            lstrcpy((TCHAR *) ((LPBYTE) m_rgpsmi[m_numpsmi] + m_rgpsmi[m_numpsmi]->dwTextOffset), pwszText);

            // Great, we added this menu item, so increase m_numpsmi
            m_numpsmi++;
        }

        // OK, is there another menu item in the pipe line?
        if (m_dwParseLen && (((*m_lpbParse) & 0x7f) == 0x0f))
        {
            // Yes, there is -- read it in and stay in the loop
            m_lpbParse++;
            m_dwParseLen--;
        }
        else
        {
            // Nope, that's it for menu items
            break;
        }
    }

Exit:
    if (FAILED(hr))
    {
        if (NULL != m_rgpsmi)
        {
            // Free memory
            for (UINT i = 0; i < m_numpsmi; i++)
            {
                if (NULL != m_rgpsmi[i])
                {
                    delete[] (BYTE*)m_rgpsmi[i];
                    m_rgpsmi[i] = NULL;
                }
            }
            delete[] m_rgpsmi;
            m_rgpsmi = NULL;
        }
        m_numpsmi = 0;
    }

    delete pwszText;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadMenuItem\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadNextAction

    PURPOSE:    Reads the next action for a menu item, assumes that m_rgpsmi
                    and m_numpsmi have already been set

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadNextAction()
{
    HRESULT hr = S_OK;
    DWORD dwLength, i;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadNextAction\r\n")));

    ASSERT((m_rgpsmi != NULL) && (m_numpsmi > 0));

    // See how many next action items there are
    dwLength = (DWORD) (*(m_lpbParse++));
    m_dwParseLen--;

    if ((dwLength > m_numpsmi) || (m_dwParseLen < dwLength))
    {
        // Nope, this is no good
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Length %d doesn't fit between %d and %d\r\n"), dwLength, m_numpsmi, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // OK, go through and set them, please
    for (i = 0; i < dwLength; i++)
    {
        m_rgpsmi[i]->dwParams |= SIM_PARAM_SIMMENUITEM_NEXTACTION;
        m_rgpsmi[i]->dwNextAction = (DWORD) (*(m_lpbParse++));
    }

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadNextAction\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadDuration

    PURPOSE:    Reads the duration from a command, setting m_dwDuration

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadDuration()
{
    HRESULT hr = S_OK;
    DWORD dwUnits;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadDuration\r\n")));

    ASSERT(m_dwDuration == SIMTONE_DEFAULTDURATION);

    // Note, we will now read 3 more bytes, so we'd better have it!
    if (m_dwParseLen < 3)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 3 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 2
    if (*(m_lpbParse++) != 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte should be 2, is %x\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Third byte is time unit, 0 = minutes, 1 = seconds, 2 = 0.1 seconds
    // Fourth byte is the value
    dwUnits = (*(m_lpbParse++));
    m_dwDuration = (*(m_lpbParse++));
    switch (dwUnits)
    {
        case SIMTKIT_POLLINTERVAL_UNIT_DURATIONMINUTES:
            m_dwDuration *= 60000;
            break;

        case SIMTKIT_POLLINTERVAL_UNIT_DURATIONSECONDS:
            m_dwDuration *= 1000;
            break;

        case SIMTKIT_POLLINTERVAL_UNIT_DURATIONTENTHS:
            m_dwDuration *= 100;
            break;

        default:
            // Illegal value
            hr = E_FAIL;
            goto Exit;
            break;
    }
    m_dwParseLen -= 3;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Duration %d, remaining length %d\r\n"), m_dwDuration, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadDuration\r\n")));

    return hr;
}

/****************************************************************************

    FUNCTION:   ReadResponseLength

    PURPOSE:    Reads the response length from a command, setting m_dwMinResponse
                    and m_dwMaxResponse

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadResponseLength()
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadResponseLength\r\n")));

    ASSERT((m_dwMinResponse == 1) && (m_dwMaxResponse == 1));

    // Note, we will now read 3 more bytes, so we'd better have it!
    if (m_dwParseLen < 3)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 3 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 2
    if (*(m_lpbParse++) != 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte should be 2, is %x\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Read the next two bytes
    m_dwMinResponse = (DWORD) *(m_lpbParse++);
    m_dwMaxResponse = (DWORD) *(m_lpbParse++);
    ASSERT(m_dwMaxResponse >= m_dwMinResponse);
    m_dwParseLen -= 3;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Response min %d, max %d, length remaining %d\r\n"), m_dwMinResponse, m_dwMaxResponse, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadResponseLength\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadItemIdentifier

    PURPOSE:    Reads the item identifier from a command, setting m_dwItemId

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadItemIdentifier()
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadItemIdentifier\r\n")));

    ASSERT(m_dwItemId == -1);

    // Note, we will now read 2 more bytes, so we'd better have it!
    if (m_dwParseLen < 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 2 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 1
    if (*(m_lpbParse++) != 1)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte is %x, should be 1\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Third byte is identifier
    m_dwItemId = (DWORD) *(m_lpbParse++);
    m_dwParseLen -= 2;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Item id is %x, length remaining %d\r\n"), m_dwItemId, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadItemIdentifier\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadSubAddress

    PURPOSE:    Reads the subaddress from a command, setting m_pwszSubAddr
                    and m_dwSubAddrLen

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadSubAddress()
{
    HRESULT hr = S_OK;
    DWORD dwLength, i;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadSubAddress\r\n")));

    ASSERT(m_pwszSubAddr == NULL);

    // OK, read in the length
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadCmdLength failed %x\r\n"), hr));
        goto Exit;
    }

    // OK, we should have this much space left
    if (m_dwParseLen < dwLength)
    {
        // Nope, not enough space left
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Now for the actual subaddress string
    // For now, I'm treating this just as a char string (see 04.08, 10.5.4.8)
    m_pwszSubAddr = new TCHAR[dwLength + 1];
    if (!m_pwszSubAddr)
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    for (i = 0; i < dwLength; i++)
    {
        m_pwszSubAddr[i] = (TCHAR) (*(m_lpbParse++));
    }
    m_pwszSubAddr[dwLength] = TEXT('\0');
    m_dwSubAddrLen = (dwLength + 1) * sizeof(TCHAR);
    m_dwParseLen -= dwLength;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Read subaddress %s, remaining length %d\r\n"), m_pwszSubAddr, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadSubAddress\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadCapability

    PURPOSE:    Reads the capabilities in

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCapability()
{
    HRESULT hr = S_OK;
    DWORD dwTLVLen, dwCapLen;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadCapability\r\n")));

    // The next byte is the length of the whole TLV, while the third byte is the
    // amount that we should actually pay attention to -- anything after that amount
    // should be ignored
    dwTLVLen = (DWORD) *(m_lpbParse++);
    dwCapLen = (DWORD) *(m_lpbParse++);
    m_dwParseLen -= 2;

    // OK, we should have at least as much as dwTLVLen - the three bytes we've already read
    if (m_dwParseLen < (dwTLVLen - 3))
    {
        // Nope, not enough space left
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), (dwTLVLen - 3), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Ignoring the next bytes (see 04.08, 10.5.4.5)
    m_lpbParse += (dwTLVLen - 3);
    m_dwParseLen -= (dwTLVLen - 3);

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadCapability\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadFileList

    PURPOSE:    Reads a filelist, setting m_pwszFileList, m_dwNumFiles, and
                    m_dwFileListLen

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadFileList()
{
    HRESULT hr = S_OK;
    DWORD i, j, dwLength;
    WORD wAddress;
    BOOLEAN fBeginningOfFiles;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadFileList\r\n")));

    ASSERT((m_pdwFiles == NULL) && (m_dwNumFiles == 0));

    // OK, read in the length
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        goto Exit;
    }

    // OK, we should have this much space left
    if (m_dwParseLen < dwLength)
    {
        // Nope, not enough space left
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // OK, how many files are there?
    m_dwNumFiles = (DWORD) *(m_lpbParse++);

    if (0 == m_dwNumFiles)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Zero files in the list.\r\n")));
        hr = E_FAIL;
        goto Exit;
    }

    // Allocate memory, please
    m_pdwFiles = new DWORD[m_dwNumFiles];
    if (!m_pdwFiles)
    {
        hr = E_FAIL;
        goto Exit;
    }

    // The first thing better be 3f, or it's invalid
    if (*m_lpbParse != 0x3f)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Expected 3F in file list, saw %x\r\n"), *m_lpbParse));
        hr = E_INVALIDARG;
        goto Exit;
    }

    // When we first enter the loop looking for files, we are sitting on a 0x3F.
    // To properly pick up the first file in the list we need to skip the first
    // look for 0x3F in the loop. This flag will tell the loop we are at the
    // beginning of the file list.
    fBeginningOfFiles = TRUE;

    // OK, read in bytes -- we're only interested in the last byte in each file
    // Note that we don't check each layer down to make sure it's valid
    // (e.g. matches GSM 11.11, sec 6.2)
    wAddress = 0;
    for (i = 0, j = 0; (i < ((dwLength - 1) / 2)) && (j < m_dwNumFiles); i++)
    {
        if (*m_lpbParse == 0x3f)
        {
            // Check if we are at the beginning of the file list. If so, we don't want
            // to record a found file yet.
            if (FALSE == fBeginningOfFiles)
            {
                // That's it, we're done with this one
                m_pdwFiles[j++] = (DWORD) wAddress;
            }
            else
            {
                // The very first pass through the loop, the parse point should be on a
                // 0x3F and fBeginningOfFiles should be true. This code should be run
                // on every first pass to reset the fBeginningOfFiles flag and allow
                // further found files to be recorded.
                fBeginningOfFiles = FALSE;
            }
        }

        // Cache this word and go to the next one
        wAddress = *(m_lpbParse++);
        wAddress = (WORD) ((wAddress << 8) + *(m_lpbParse++));
    }

    // If we have looked through the entire file list and do not have enough files yet,
    // we need to add the last file.
    if ( (i >= ((dwLength - 1) / 2)) && (j < m_dwNumFiles) )
    {
        m_pdwFiles[j] = (DWORD) wAddress;
    }

    // Adjust the length
    m_dwParseLen -= dwLength;

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadFileList\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadProvisionFileList

    PURPOSE:    Parses a variable number of Provision file references

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadProvisionFileList()
{
    HRESULT hr = S_OK;
    DWORD dwFileLength;
    DWORD dwRefSize;
    LPBYTE lpbFile = NULL;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadProvisionFileList\r\n")));

    ASSERT((m_rgpsfr == NULL) && (m_numpsfr == 0));

    // Allocate a range of file references to work with.
    m_rgpsfr = new LPSIMFILEREFERENCE[10];
    if(NULL == m_rgpsfr)
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    memset(m_rgpsfr, 0, sizeof(LPSIMFILEREFERENCE) * 10);

    // Loop as long as provisioning file references exists
    for (;;)
    {
        // Provisioning file references look just like a byte list.
        // Memory allocated for us in lpbData.
        lpbFile = NULL;
        dwFileLength = 0;
        ReadCommonByteList(&lpbFile, &dwFileLength);

        if (0 >= dwFileLength && NULL == lpbFile)
        {
            // We did not parse anything useful which is unexpected, but exit gracefully.
            DEBUGCHK(FALSE);
            goto Exit;
        }

        // Do we need to grow the array of files?
        if (m_numpsfr && ((m_numpsfr % 10) == 0))
        {
            // Yes, re-allocate
            LPSIMFILEREFERENCE *psfrTemp = new LPSIMFILEREFERENCE[m_numpsfr + 10];

            if (!psfrTemp)
            {
                hr = E_OUTOFMEMORY;
                goto Exit;
            }

            memcpy(psfrTemp, m_rgpsfr, m_numpsfr * sizeof(LPSIMFILEREFERENCE));
            delete[] m_rgpsfr;
            m_rgpsfr = psfrTemp;
        }

        // Now we can allocate memory
        dwRefSize = ROUNDTODWORD(sizeof(SIMFILEREFERENCE)) + ROUNDTODWORD(dwFileLength);
        m_rgpsfr[m_numpsfr] = NULL;
        m_rgpsfr[m_numpsfr] = (SIMFILEREFERENCE *) new BYTE[dwRefSize];

        if (NULL != m_rgpsfr[m_numpsfr])
        {
            memset(m_rgpsfr[m_numpsfr], 0, dwRefSize);
            m_rgpsfr[m_numpsfr]->cbSize = dwRefSize;
            m_rgpsfr[m_numpsfr]->dwParams = SIM_PARAM_SIMFILEREFERENCE_ALL;
            m_rgpsfr[m_numpsfr]->dwFileSize = dwFileLength;
            m_rgpsfr[m_numpsfr]->dwFileOffset = ROUNDTODWORD(sizeof(SIMFILEREFERENCE));
            memcpy(((LPBYTE) m_rgpsfr[m_numpsfr] + m_rgpsfr[m_numpsfr]->dwFileOffset), lpbFile, dwFileLength);

            // Great, we added this file, so increase the count
            m_numpsfr++;
        }

        // Check for another Provisioning file reference.
        // OK, is there another file in the pipe line?
        if (m_dwParseLen && (((*m_lpbParse) & 0x7f) == PROVISIONFILETAG))
        {
            // Yes, there is -- read it in and stay in the loop
            m_lpbParse++;
            m_dwParseLen--;
        }
        else
        {
            // No, that's it for provision file references
            break;
        }
    }

Exit:
    if (FAILED(hr))
    {
        if (NULL != m_rgpsfr)
        {
            // Free memory
            for (UINT i = 0; i < m_numpsfr; i++)
            {
                if (NULL != m_rgpsfr[i])
                {
                    delete[] (BYTE*) m_rgpsfr[i];
                    m_rgpsfr[i] = NULL;
                }
            }
            delete[] m_rgpsfr;
            m_rgpsfr = NULL;
        }
        m_numpsfr = 0;
    }

    if (NULL != lpbFile)
    {
        delete[] lpbFile;
        lpbFile = NULL;
    }

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadProvisionFileList\r\n")));

    return hr;
}
/****************************************************************************

    FUNCTION:   ReadBufferSize

    PURPOSE:    Reads in the buffer size

    RETURNS:    HRESULT

****************************************************************************/
HRESULT CRilSimToolkitCommand::ReadBufferSize()
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadBufferSize\r\n")));

    // Note, we will now read 3 more bytes, so we'd better have it!
    if (m_dwParseLen < 3)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 3 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 2
    if (*(m_lpbParse++) != 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte is %x, should be 2\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Buffer size is encoded as 2 bytes. Next byte is the most significant.
    m_dwBufferSize = (*(m_lpbParse++) << 8);
    // Add in the least significant byte.
    m_dwBufferSize += *(m_lpbParse++);
    m_dwParseLen -= 3;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Byte is 0x%x, length remaining %d\r\n"), m_dwBufferSize, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadBufferSize\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadTransportLevel

    PURPOSE:    Reads in the tranport protocol type and port number from
                an interface transport level tag

    RETURNS:    HRESULT

****************************************************************************/
HRESULT CRilSimToolkitCommand::ReadTransportLevel()
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadTransportLevel\r\n")));

    // Note, we will now read 4 more bytes, so we'd better have it!
    if (m_dwParseLen < 4)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 4 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 3
    if (*(m_lpbParse++) != 3)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte is %x, should be 3\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Protocol type  is encoded in the next byte.
    m_dwProtocolType = *(m_lpbParse++);

    // Port number is encoded as 2 bytes. Next byte is the most significant.
    m_dwPortNum = (*(m_lpbParse++) << 8);
    // Add in the least significant byte.
    m_dwPortNum += *(m_lpbParse++);

    m_dwParseLen -= 4;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Type is 0x%x port numb is 0x%x, length remaining %d\r\n"), m_dwProtocolType, m_dwPortNum, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadTransportLevel\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadIconIdentifier

    PURPOSE:    Reads the icon identifier for a command, setting
                m_dwIconIdentifier and m_dwIconQualifier

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadCommonIconIdentifier(LPDWORD pdwIconIdentifier, LPDWORD pdwIconQualifier)
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadIconIdentifier\r\n")));


    if (NULL == pdwIconIdentifier || NULL == pdwIconQualifier)
    {
        ASSERT(FALSE);
        hr = E_INVALIDARG;
        goto Exit;
    }

    // Note, we will now read 3 more bytes, so we'd better have it!
    if (m_dwParseLen < 3)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 3 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 2
    if (*(m_lpbParse++) != 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte is %x, should be 2.\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Third byte is the icon qualifier
    *pdwIconQualifier = (DWORD) *(m_lpbParse++);

    // Fourth byte is the icon Identifier
    *pdwIconIdentifier = (DWORD) *(m_lpbParse++);

    m_dwParseLen -= 3;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Item id is %x, length remaining %d\r\n"), m_dwItemId, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadIconIdentifier\r\n")));
    return hr;
}


/****************************************************************************

    FUNCTION:   ReadIconIdentifierList

    PURPOSE:    Reads the icon identifier list for a corresponding set of
                SETUP MENU/SELECT ITEM menu items. The list of menu items
                in m_rgpsmi will be iterated through, add the icon
                information to each of the menu items.

    PARAMETERS: None

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadIconIdentifierList()
{
    BYTE bLength;
    BYTE bIconListQualifier;
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadIconIdentifierList\r\n")));

    // Make sure there is a list of menu items.
    if (NULL == m_rgpsmi)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadIconIdentifierList: Menu items list does not exist.\r\n")));
        hr = E_FAIL;
        goto Exit;
    }

    // At least the length and icon list qualifier is needed. Make sure there is enough space left
    // to parse.
    if (m_dwParseLen < 2)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 2 bytes, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length.
    bLength = *(m_lpbParse);

    // Third byte is the icon list qualifier
    bIconListQualifier = *(m_lpbParse + 1);

    // The remaining length should be consistent with the number of menu items.
    if ((bLength - 1) != m_numpsmi)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadIconIdentifierList: Icon id list count does not match the number of menu items.\r\n")));
        hr = E_FAIL;
        goto Exit;
    }

    // Make sure there is enough space left to parse
    if ((m_dwParseLen - 2) < m_numpsmi)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadIconIdentifierList: Need %d bytes, only have %d bytes left to parse.\r\n"), m_numpsmi, (m_dwParseLen - 2)));
        hr = E_FAIL;
        goto Exit;
    }

    // Add each icon identifier to the associated menu item.
    LPBYTE pbCurIconId = (m_lpbParse + 2);
    for (DWORD dwCurMenuItem = 0; dwCurMenuItem < m_numpsmi; dwCurMenuItem++)
    {
        m_rgpsmi[dwCurMenuItem]->dwParams |= (SIM_PARAM_SIMMENUITEM_ICONID | SIM_PARAM_SIMMENUITEM_ICONQUALIFIER);
        m_rgpsmi[dwCurMenuItem]->dwIconIdentifier = *pbCurIconId;
        m_rgpsmi[dwCurMenuItem]->dwIconQualifier = bIconListQualifier;
        pbCurIconId++;
    }

    // Marked the data as parsed. (Length plus size of length.)
    m_dwParseLen -= bLength + 1;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Item id is %x, length remaining %d\r\n"), m_dwItemId, m_dwParseLen));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadIconIdentifierList\r\n")));
    return hr;
}


/****************************************************************************

    FUNCTION:   ReadImmediate

    PURPOSE:    Reads whether an immediate response to Display Text is
                requested.

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadImmediate()
{
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadImmediate\r\n")));

    // Note, we will now read 1 more byte.
    if (m_dwParseLen < 1)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Need 1 byte, only have %d\r\n"), m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    // Second byte is length, which should be 0
    if (*(m_lpbParse++) != 0)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Second byte (length) is %x, should be 0.\r\n"), *(m_lpbParse - 1)));
        hr = E_FAIL;
        goto Exit;
    }

    // Set immediate response
    m_fImmediateResponse = TRUE;
    m_dwParseLen -= 1;
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: Immediate response set.\r\n")));

Exit:
    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadImmediate\r\n")));
    return hr;
}

/****************************************************************************

    FUNCTION:   ReadDTMF

    PURPOSE:    Reads a DTMF string.

    RETURNS:    HRESULT

****************************************************************************/

HRESULT CRilSimToolkitCommand::ReadDTMF()
{
    DWORD dwLength;
    BOOL fConverted;
    TCHAR* pwsz = NULL;
    int iConvertedSize = 0;
    HRESULT hr = S_OK;

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: +CRilSimToolkitCommand::ReadDTMF\r\n")));

    // First read the length of the text
    hr = ReadCmdLength(&dwLength);
    if (FAILED(hr))
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadDTMF failed %x\r\n"), hr));
        hr = E_FAIL;
        goto Exit;
    }

    if (m_dwParseLen < dwLength)
    {
        // Well, that can't be
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadDTMF Expected %d bytes, only have %d\r\n"), dwLength, m_dwParseLen));
        hr = E_FAIL;
        goto Exit;
    }

    if (!dwLength)
    {
        // We've received a null text string which is valid, No further work is necessary,
        // so just return a positive result.
        hr = S_OK;
        goto Exit;
    }

    // Allocate enough space to hold the converted string.
    pwsz = new TCHAR[(2 * dwLength) + 1];
    if (NULL == pwsz)
    {
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    fConverted = ConvertSemiOctetToWCHAR(m_lpbParse,
                                         (BYTE) (2 * dwLength),
                                         pwsz,
                                         (2 * dwLength) + 1,
                                         &iConvertedSize);
    if (!fConverted)
    {
        RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: ReadDTMF could not convert the DTMF value.\r\n")));
        hr = E_FAIL;
        goto Exit;
    }

    pwsz[(2 * dwLength)] = TEXT('\0');
    m_pwszDTMF = pwsz;
    m_dwDTMFLen = ((iConvertedSize * 2) + 1) * sizeof(TCHAR);

    m_lpbParse += dwLength;
    m_dwParseLen -= dwLength;

Exit:
    if (FAILED(hr))
    {
        if (NULL != pwsz)
        {
            delete[] pwsz;
        }
    }

    RETAILMSG(MSG_ON, (TEXT("RilDrv : SIMTKit: -CRilSimToolkitCommand::ReadDTMF\r\n")));
    return hr;
}



